﻿using AngleSharp;
using Application.Common.Interface;
using Core.Extensions;
using Domain.Model;
using Microsoft.Extensions.DependencyInjection;
using MiniExcelLibs;
using System.Collections.Concurrent;

namespace Core.Persistence.Repository
{
    public class WebScraperRepository : IWebScraperRepository
    {
        private readonly IServiceProvider provider;

        public WebScraperRepository(IServiceProvider provider)
        {
            this.provider = provider;
        }
        public async Task Scraper(SiteMap siteMap, Selector root)
        {

            using var dbContext = provider.CreateScope().ServiceProvider.GetService<WebScraperDbContext>();
            if (dbContext is null)
                throw new Exception($"can`t get service {nameof(WebScraperDbContext)}");
            try
            {
                var config = AngleSharp.Configuration.Default.WithDefaultLoader();
                var context = BrowsingContext.New(config);
                {
                    var allUrl = siteMap.Url.UrlGetAllPage();
                    List<string> propDicIndex = new();
                    List<ConcurrentDictionary<string, List<string>>> allDataDic = new();
                    foreach (var url in allUrl)
                    {
                        //所有属性
                        var curData = await GetDataAsync(context, url, root, "");
                        if (curData.Any())
                        {
                            foreach (var propValueList in curData)
                            {
                                var curPropName = propValueList.Keys.First();
                                if (!propDicIndex.Contains(curPropName))
                                {
                                    propDicIndex.Add(curPropName);
                                    allDataDic.Add(propValueList);
                                }
                                else
                                {
                                    allDataDic[propDicIndex.IndexOf(curPropName)][curPropName].AddRange(propValueList[curPropName]);
                                }
                            }

                        }
                    }
                    var path = $"{siteMap.Name}.xlsx";
                    var endData = ConvertMiniExcelData(allDataDic);
                    if (File.Exists(path))
                        File.Delete(path);
                    MiniExcel.SaveAs(path, endData);
                }
            }
            catch (Exception e)
            {
                throw;
            }
            finally
            {
                siteMap.isscraper = false;
                dbContext.Update(siteMap);
                dbContext.SaveChanges();
            }
        }
        public async Task<List<ConcurrentDictionary<string, List<string>>>> GetDataAsync(IBrowsingContext context, string Url, Selector father, string startSelector)
        {
            List<ConcurrentDictionary<string, List<string>>> res = new();
            foreach (var child in father.Childrens)
            {
                if (child.Type == SelectorType.Element)
                {
                    res.AddRange(await GetDataAsync(context, Url, child, child.Selector));
                }
                else if (child.Type == SelectorType.Text)
                {
                    var document = await context.OpenAsync(Url);
                    var cells = document.QuerySelectorAll($"{startSelector} {child.Selector}");
                    var titles = cells.Select(m => m.TextContent);
                    var curprop = new ConcurrentDictionary<string, List<string>>();
                    curprop.TryAdd(child.Name, titles.ToList());
                    res.Add(curprop);
                }
                else if (child.Type == SelectorType.Link)
                {
                    {
                        var document = await context.OpenAsync(Url);
                        var cells = document.QuerySelectorAll($"{startSelector} {child.Selector}").Where(x => x.HasAttribute("href")).Select(x => x.GetAttribute("href"));
                        //保存当前内容
                        ConcurrentDictionary<string, List<string>> curLink = new();
                        curLink.TryAdd(child.Name, cells.ToList());
                        res.Add(curLink);

                        //保存下级详细内容
                        var curAction = document.Url[..document.Url.LastIndexOf("/")];
                        List<ConcurrentDictionary<string, List<string>>> link_detail = new();
                        foreach (var href in cells)
                        {
                            link_detail.AddRange(await GetDataAsync(context, $"{curAction}/{href}", child, ""));
                        }
                        var curprop = new ConcurrentDictionary<string, List<string>>();
                        foreach (var link in link_detail)
                        {
                            foreach (var key in link.Keys)
                            {
                                curprop.TryAdd(key, new());
                                curprop[key].AddRange(link[key]);
                            }
                        }
                        res.Add(curprop);
                    }

                }
            }
            return res;
        }

        public List<object> ConvertMiniExcelData(List<ConcurrentDictionary<string, List<string>>> propDictList)
        {
            {
                //Dictionary<string,List<string>>,{name,["zhangsan","lisi"]}
                //Dictionary<string, List<string>>{age,["13","41"]}

                //convert

            }
            List<object> res = new();
            if (propDictList.Any())
            {
                List<string> props = propDictList.Select(dict => dict.Keys.First()).ToList();
                int valueCount = propDictList.First().Values.First().Count;
                for (int i = 0; i < valueCount; i++)//多个行数据
                {
                    var row = new Dictionary<string, object>();//属性-属性值
                    for (int j = 0; j < props.Count; j++)//每个属性
                    {
                        var propValue = propDictList[j].Values.First().ToArray();
                        if (propValue.Count() > i)
                            row.Add(props[j], propValue[i]);
                        else
                            row.Add(props[j], default);
                    }
                    res.Add(row);
                }
            }
            return res;
        }

        public List<string> GetColumns(Selector root)
        {
            if (root is null || root.Childrens is null || !root.Childrens.Any())
            {
                return null;
            }
            List<string> res = new();
            foreach (var it in root.Childrens)
            {
                if (it.Childrens.Any())
                    res.AddRange(GetColumns(it));
                else
                    res.Add(it.Name);
            }
            return res;
        }

    }
}
